package com.tutu.mybatis.dynamic.datasource.config;

import com.tutu.mybatis.dynamic.datasource.bean.User;
import com.tutu.mybatis.dynamic.datasource.service.UserService;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.sql.DataSource;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author 丘友垚
 * @version 1.0
 * @date 2020/9/23 19:32
 */
@Configuration
public class DynamicSourceConfig implements ApplicationContextAware {

    private static final Logger logger = LoggerFactory.getLogger(DynamicSourceConfig.class);
    @Resource
    private UserService userService;
    @Resource
    private DynamicRoutingDataSource dynamicRoutingDataSource;

    public static ApplicationContext applicationContext;

    @PostConstruct
    public void initDatasource() {
        List<User> list = userService.list();
        ConfigurableApplicationContext applicationContext = (ConfigurableApplicationContext) DynamicSourceConfig.applicationContext;
        DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext.getBeanFactory();
        Map<Object, DataSource> dataSourceMap = dynamicRoutingDataSource.getResolvedDataSources();
        Map<Object, Object> map = new HashMap<>(dataSourceMap);
        list.forEach(user -> {
            BeanDefinitionBuilder beanDefinitionBuilder = buildDatasource(user.getId(), user.getDatasourceUsername(), user.getDatasourcePassword(), user.getDatasourceUrl());
            String beanName = DynamicRoutingDataSourceHolder.buildKey(user.getId());
            AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getRawBeanDefinition();
            beanFactory.registerBeanDefinition(beanName, beanDefinition);
            DataSource dataSource = applicationContext.getBean(beanName, DataSource.class);
            map.put(beanName, dataSource);
        });
        dynamicRoutingDataSource.setTargetDataSources(Collections.unmodifiableMap(map));
        logger.info("dynamic datasource init success");
    }

    /**
     * 构建数据源
     *
     * @param userId   userId
     * @param username username
     * @param password password
     * @param url      url
     * @return BeanDefinitionBuilder
     */
    public static BeanDefinitionBuilder buildDatasource(Long userId, String username, String password, String url) {
        HikariConfig hikariConfig = new HikariConfig();
        hikariConfig.setUsername(username);
        hikariConfig.setJdbcUrl(url);
        hikariConfig.setPassword(password);
        hikariConfig.setDriverClassName("com.mysql.cj.jdbc.Driver");
        hikariConfig.setPoolName(userId + "_HikariPool");
        return BeanDefinitionBuilder.genericBeanDefinition(HikariDataSource.class, () -> new HikariDataSource(hikariConfig));
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        DynamicSourceConfig.applicationContext = applicationContext;
    }
}
